package com.ypj.rpc.serializer;


import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.ypj.rpc.entity.RpcRequest;
import com.ypj.rpc.entity.RpcResponse;
import com.ypj.rpc.enumeration.SerializerCode;
import com.ypj.rpc.exception.SerializeException;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * JSON序列化器对某个类达到属性反序列化时，如果属性声明为Object就会造成反序列化出错，通常会把Object属性反序列化为String
 * 并且JSON序列化器是基于字符串的占用空间大且速度较慢
 *
 * Kryo基于字节的序列化对空间利用率高，序列化时记录属性对象的类型信息
 *
 * 可能存在线程安全问题，推荐放在ThreadLocal里
 */
@Slf4j
public class KryoSerializer implements CommonSerializer {

    private static final ThreadLocal<Kryo> kryoThreadLocal=ThreadLocal.withInitial(()->{
        Kryo kryo=new Kryo();
        kryo.register(RpcResponse.class);
        kryo.register(RpcRequest.class);
        kryo.setReferences(true);
        kryo.setRegistrationRequired(false);
        return kryo;
    });

    @Override
    public byte[] serialize(Object msg) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             Output output=new Output(byteArrayOutputStream)){
            Kryo kryo = kryoThreadLocal.get();
            kryo.writeObject(output,msg);
            kryoThreadLocal.remove();
            return output.toBytes();
        } catch (Exception e) {
            log.error("序列化时有错误发生：",e);
            throw new SerializeException("序列化时发生错误！");
        }
    }

    @Override
    public Object deserialize(byte[] bytes, Class<?> clazz) {
        try ( ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
              Input input=new Input(byteArrayInputStream)){
            Kryo kryo = kryoThreadLocal.get();
            Object object = kryo.readObject(input, clazz);//传入对象类型，不需要传入每一个属性的类型信息
            kryoThreadLocal.remove();
            return object;
        } catch (Exception e) {
            log.error("反序列化时有错误发生：",e);
            throw new SerializeException("反序列化时发生错误！");
        }
    }

    @Override
    public int getCode() {
        return SerializerCode.valueOf("KRYO").getCode();
    }
}
